package com.tmall.testgen.client.runinspring.push.agent;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.regex.Pattern;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

public class ClientTestAopAgent implements ClassFileTransformer{
    
  //  premain
public static void agentmain(String args, Instrumentation inst)throws Exception{ 
    System.out.println("开始拦截");
        inst.addTransformer(new ClientTestAopAgent(),true);
         //已经加载的类开始拦截
        for (Class clazz :inst.getAllLoadedClasses()){ 
            try{
                if(isClassHandle(clazz.getName())){
                    inst.retransformClasses(clazz);
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        } 
        
        
    }  

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        String classStandardName=className.replaceAll("/", ".");
        if(!isClassHandle(classStandardName)){
            return null;
        }
        try {
            return  rewriteClass(loader, classStandardName, classBeingRedefined, protectionDomain, classfileBuffer);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    

    public byte[] rewriteClass(ClassLoader loader, String classStandardName,
            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
            byte[] classfileBuffer) throws Exception{
        
        // 用于取得字节码类，必须在当前的classpath中，使用全称
        
          ClassPool pool = ClassPool.getDefault();
        
        
        CtClass  ctClass=   pool.get(classStandardName);
        CtMethod[]  methods=ctClass.getDeclaredMethods();
        
        
        System.out.println("methods"+methods.length);
        if(methods==null||methods.length==0)
            return null;
        
        for(CtMethod method:methods){
            handleMethod(classStandardName,method);
        }
        
        return ctClass.toBytecode();
        
        
    }
    
    private static boolean isMatchClass(String regEx,String source){
        
        if(regEx==null||regEx.trim().length()==0)
            return false;
        if(regEx.indexOf("$")>0){
            regEx=regEx.substring(0, regEx.indexOf("$"));
        }
        Pattern sp=Pattern.compile(regEx);
        return sp.matcher(source).matches();
    }
    
  
    private static  boolean isClassHandle(String className){
       String mockdes;
        try {
            mockdes = NeedAop.getMockDes();
        } catch (Exception e) {
            return false;
        }
       
       return isMatchClass(mockdes,className);
    }
    
    
    public void handleMethod(String   classStandardName,CtMethod method) throws Exception{


        MethodInfo methodInfo = method.getMethodInfo(); 
        if(methodInfo.getAccessFlags()!=AccessFlag.PUBLIC)
            return;
        
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();  
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); 
            
        int length = method.getParameterTypes().length;  
        String paramNames="";
        int pos = Modifier.isStatic(method.getModifiers()) ? 0 : 1; 
        if(attr!=null){
            for (int i = 0; i < length; i++)  {
                paramNames+=attr.variableName(i + pos)+";"; 
            }
        }
        
        CtClass[]  types=method.getParameterTypes();
        int len=types==null?0:types.length;
        String[] ty=new String[len];
        String tyStr="";
        if(types!=null){
            for(int i=0;i<types.length;i++){
                ty[i]=types[i].getSimpleName();
                tyStr+=types[i].getSimpleName()+";";
            }
            
        }
        
        String returnType= method.getReturnType().getSimpleName();
        String methodShow=classStandardName+"."+method.getName()+getMethodParamContentContent(paramNames.split(";"), ty,returnType);
        
        String result="testGenAopObj";
        String resultCode=getReturn(method.getReturnType().getName(),result);
        System.out.println(methodShow+","+resultCode);
        
        method.insertBefore("{ Object "+result+"= com.tmall.testgen.client.runinspring.push.agent.NeedAop.getAopResult(\""+methodShow+"\",$args); if("+result+"!=null) return "+resultCode+"; } ");
        
        
    }
    private String getReturn(String type,String result){
        if("int".equals(type))
            return "Integer.valueOf("+result+"+\"\").intValue()";
        if("float".equals(type))
            return "Float.valueOf("+result+"+\"\").floatValue()";
        if("double".equals(type))
            return "Double.valueOf("+result+"+\"\").doubleValue()";
        if("long".equals(type))
            return "Long.valueOf("+result+"+\"\").longValue()";
        
        return "("+type+")"+result;
    }
    public static String getMethodParamContentContent(String[] paramNames,String[] paramTypes,String returnType){
        StringBuilder sb=new StringBuilder();
        if(paramTypes==null||paramTypes.length==0)
            return "( ):"+returnType;
        for(int i=0;i<paramNames.length;i++){
            sb.append(paramTypes[i]+" "+paramNames[i]+",");
        }
        
        String totalShowName=sb.toString();
        if(totalShowName.endsWith(",")){
            totalShowName=totalShowName.substring(0, totalShowName.length()-1);
        }
        
        
        return "("+totalShowName+"):"+returnType;
    }


}
